package control;

import entities.Administrator;
import entities.Contest;
import entities.Country;
import entities.Institution;
import entities.KeyWord;
import entities.Problem;
import entities.ProblemStats;
import entities.Run;
import entities.Submission;
import entities.TestCase;
import entities.User;
import entities.UserStats;
import java.util.ArrayList;
import java.util.List;
import services.*;
import util.VerdictOutput;
import vo.*;

public class Controller
{

    private static Controller INSTANCE = null;
    public static CountryService cs;
    public static InstitutionService is;
    public static UserService us;
    public static UserStatsService uss;
    public static ProblemService ps;
    public static ProblemStatsService pss;
    public static AdministratorService as;
    public static ContestService cts;
    public static KeyWordService ks;
    public static TestCaseService ts;
    public static SubmissionService ss;
    public static RunService rs;

    private Controller()
    {
    }

    public static Controller getInstance()
    {
        if (INSTANCE == null)
        {
            INSTANCE = new Controller();
        }
        return INSTANCE;
    }

     private UserService getUserService()
    {
        if (us == null)
        {
            us = new UserService();
        }
        return us;
    }
    private ProblemService getProblemService()
    {
        if (ps == null)
        {
            ps = new ProblemService();
        }
        return ps;
    }
    private KeyWordService getKeyWordService()
    {
        if (ks == null)
        {
            ks = new KeyWordService();
        }
        return ks;
    }
    private ProblemStatsService getProblemStatsService()
    {
        if (pss == null)
        {
            pss = new ProblemStatsService();
        }
        return pss;
    }
    private UserStatsService getUserStatsService()
    {
        if (uss == null)
        {
            uss = new UserStatsService();
        }
        return uss;
    }
    private SubmissionService getSubmissionService()
    {
        if (ss == null)
        {
            ss = new SubmissionService();
        }
        return ss;
    }
    private ContestService getContestService()
    {
        if (cts == null)
        {
            cts = new ContestService();
        }
        return cts;
    }
    private AdministratorService getAdminService()
    {
        if (as == null)
        {
            as = new AdministratorService();
        }
        return as;
    }
    private TestCaseService getTestService()
    {
        if (ts == null)
        {
            ts = new TestCaseService();
        }
        return ts;
    }
    private CountryService getCountryService()
    {
        if (cs == null)
        {
            cs = new CountryService();
        }
        return cs;
    }
    private InstitutionService getInstitutionService()
    {
        if (is == null)
        {
            is = new InstitutionService();
        }
        return is;
    }

    private RunService getRunService()
    {
        if (rs == null)
        {
            rs = new RunService();
        }
        return rs;
    }



    public static void main(String[] args)
    {
        Controller c = new Controller();
        // Both countries and institutions are going to be handled from the db
        /*cs = new CountryService();
        cs.saveCountry("Colombia");
        is = new InstitutionService();
        is.saveInstitution("UN", true, "Colombia");

        // creating a VOUser : The view must do this
        VOUser vuser  = new VOUser();
        vuser.setEmail("email");
        vuser.setFirstName("Santiago");
        vuser.setHandle("sbaldrich");
        vuser.setLastName("Baldrich");
        vuser.setPassword("baldrichpass");
        vuser.setInstName("UN");
        c.registerUser(vuser);

        VOProblem vprob = new VOProblem();
        vprob.setName("ElProblema");
        vprob.setDifficulty(5);
        vprob.setDescription("Descripción Problema");
        vprob.setTime(1);*/


        try
        {
            //c.addProblem(vprob);
            //c.addKeyWord("Binary Search");
            //c.addKeyWordToProblem("Binary Search", "ElProblema");
            //c.removeKeyWordFromProblem("Binary Search", "ElProblema");
            //c.deleteKeyWord("Binary Search");
            System.out.println(c.deleteProblem("ElProblema"));
            //System.out.println(c.deleteUser("sbaldrich"));
        }
        catch (Exception e)
        {
            System.out.println("Exception at main: " + e.getMessage());
        }

    }


    public List<VOCountry> listCountries()
    {
        cs = getCountryService();
        List<Country> cl = cs.findAll();
        if(cl == null)
            return null;
        List<VOCountry> l = new ArrayList<VOCountry>();
        for(Country c : cl)
            l.add(c.getVO());
        return l;
    }
    public boolean removeKeyWordFromProblem(String name, String pName)throws Exception
    {
        ps = getProblemService();
        ks = getKeyWordService();
        Problem p = ps.getProblem(pName);
        KeyWord k = ks.getKeyWord(name);
        if(p == null)throw new Exception("Problem doesn't exist");
        if(k == null)throw new Exception("Keyword doesn't exist");
        return ps.removeKeyWord(p,k);

    }

    public boolean deleteKeyWord(String name)throws Exception
    {
        ks = getKeyWordService();
        KeyWord k = ks.getKeyWord(name);
        if(k == null)
            throw new Exception("KeyWord doesn't exist");
        return ks.deleteKeyWord(k);
    }

    public boolean createContest(VOContest vo)
    {
        cts = getContestService();
        return cts.saveContest(vo.getContestID(), vo.getName(), vo.getStartTime(), vo.getEndTime(), vo.getUser().getHandle());
    }


    public boolean addProblemToContest(String cID, String pName)throws Exception
    {
        ps = getProblemService();
        cts = getContestService();
        Problem p = ps.getProblem(pName);
        Contest c = cts.getContest(cID);
        if(p == null)throw new Exception("Problem doesn't exist");
        if(c == null)throw new Exception("Contest doesn't exist");
        return cts.addProblem(c,p);
    }

    public boolean removeProblemFromContest(String cID, String pName)throws Exception
    {
        ps = getProblemService();
        cts = getContestService();
        Problem p = ps.getProblem(pName);
        Contest c = cts.getContest(cID);
        if(p == null)throw new Exception("Problem doesn't exist");
        if(c == null)throw new Exception("Contest doesn't exist");
        return cts.removeProblem(c,p);
    }
    

    public VOContest getContestByContestId(String contestId)throws Exception
    {
        cts = getContestService();
        Contest c = cts.getContest(contestId);
        if(c == null)
            throw new Exception("Contest doesn't exist");
        return c.getVO();
    }

    public VOContest getContestByName(String contestName)throws Exception
    {
        cts = getContestService();
        Contest c = cts.getContestByName(contestName);
        if(c == null)
            throw new Exception("Contest not found");
        return c.getVO();
    }

    public List<VOProblem> getProblemsByContest(String contestID)throws Exception
    {
        cts = getContestService();
        Contest c = cts.getContest(contestID);
        if (c == null)
            throw new Exception("Contest doesn't exist");
        ArrayList<VOProblem> l = new ArrayList<VOProblem>();
        for(Problem p : c.getProblemCollection() )
            l.add(p.getVO());
        return l;
    }

    public String submitProblem(VOSubmission vo)throws Exception
    {
        us = getUserService();
        ps = getProblemService();
        ss = getSubmissionService();
        ts = getTestService();
        rs = getRunService();

        User u = us.getUser(vo.getUserHandle());
        Problem p = ps.getProblem(vo.getProblemName());
        if(u == null)
            throw new Exception("User doesn't exist");
        if(p == null)
            throw new Exception("Problem doesn't exist");
        Submission s = new Submission();
        s.setVOData(vo);
        s.setUser(u);
        s.setProblem(p);
        if(vo.getContestID().compareTo("")!= 0)
        {
            cts = getContestService();
            Contest c = cts.getContest(vo.getContestID());
            if(c == null)
                throw new Exception("Can't retrieve contest");
            s.setContest(c);
        }
        ArrayList<TestCase> testlist = new ArrayList<TestCase>();
        for(TestCase t: p.getTestCaseCollection())
            testlist.add(t);
        VerdictOutput verd = ss.processSubmission(s, testlist);
        s.setVerdict(verd.getVerdict());
        ss.saveSubmission(s);
        for(Run r : verd.getRuns())
            rs.saveRun(r);

        //******************Just testing!
        return s.getVerdict();
    }
    /*
    
    public List<User> listRankedUsers()
    {

    }

    
   
    public List<VOSubmission> generateScoreBoard(String contestId)
    {
        ss = getSubmissionService();
        
    }
    */

       

// Edit Operations

   public boolean editContest(VOContest vo)
    {
        cts = getContestService();
        Contest c = cts.getContest(vo.getIdContest());
        c.setVOData(vo);
        return cts.editContest(c);
    }
    public boolean editKeyWord(VOKeyWord vo)
    {
        ks = getKeyWordService();
        KeyWord k = ks.getKeyWord(vo.getIdKeyWord());
        k.setName(vo.getName());
        return ks.editKeyWord(k);
    }
    public boolean editTestCase(VOTestCase vo)
    {
        ts = getTestService();
        TestCase t = ts.getTestCase(vo.getIdTestCase());
        t.setVOData(vo);
        return ts.editTestCase(t);
    }
    public boolean editProblem(VOProblem vo)
    {
        ps = getProblemService();
        Problem p = ps.getProblem(vo.getIdProblem());
        p.setVOData(vo);
        return ps.editProblem(p);
    }
    public boolean updateUserInfo(VOUser vo)
    {
        us = getUserService();
        User u = us.getUser(vo.getHandle());
        u.setVOData(vo);
        return us.editUser(u);
    }

    public boolean updateAdminInfo(VOAdministrator vo)
    {
        as = getAdminService();
        Administrator a = as.getAdministrator(vo.getHandle());
        a.setVOData(vo);
        return as.editAdministrator(a);
    }

    //************************
    public boolean addTestCase(VOTestCase vo)
    {
        ts = getTestService();
        return ts.saveTestCase(vo);
    }

    public boolean deleteTestCase(int idTestCase)throws Exception
    {
        ts = getTestService();
        TestCase t = ts.getTestCase(idTestCase);
        if(t == null)
            throw new Exception("TestCase doesn't exist");
        return ts.deleteTestCase(t);
    }

    public boolean addKeyWordToProblem(String name, String pName)throws Exception
    {
        ps = getProblemService();
        ks = getKeyWordService();
        Problem p = ps.getProblem(pName);
        KeyWord k = ks.getKeyWord(name);
        if(p == null)throw new Exception("Problem doesn't exist");
        if(k == null)throw new Exception("Keyword doesn't exist");
        return ps.addKeyword(p,k);
    }

    public boolean addProblem(VOProblem vo) throws Exception
    {
        ps = getProblemService();
        if(ps.getProblem(vo.getName()) != null)throw new Exception("Problem name already in use");
        return ps.saveProblem(vo);
    }
    public boolean addKeyWord(String name)throws Exception
    {
        ks = getKeyWordService();
        if(ks.getKeyWord(name)!=null)throw new Exception("Keyword already exists");
        return ks.saveKeyWord(name);
    }
    public boolean deleteProblem(String name)throws Exception
    {
        ps = getProblemService();
        Problem p = ps.getProblem(name);
        if(p == null)
            throw new Exception("Problem doesn't exist");
        return ps.deleteProblem(ps.getProblem(name));
    }
    public boolean deleteUser(String handle)throws Exception
    {
        us = getUserService();
        User u = us.getUser(handle);
        if(u == null)
            throw new Exception("User doesn't exist");
        return us.deleteUser(u);
    }
    public List<VOSubmission> findSubmissionsByUser(int idUser)throws Exception
    {
        ss = getSubmissionService();
        us = getUserService();
        User u = us.getUser(idUser);
        if(u == null)throw new Exception("User doesn't exist");
        List<VOSubmission> l = new ArrayList<VOSubmission>();
        for(Submission s : ss.getSubmissionsByUser(u))
            l.add(s.getVO());
        return l;
    }
    public VOAdministrator getAdmin(String handle)throws Exception
    {
        as = getAdminService();
        Administrator a = as.getAdministrator(handle);
        if(a == null)
            throw new Exception("Admin doesn't exist");
        return a.getVO();
    }
    
    public VOUser getUser(String handle)throws Exception
    {
        us = getUserService();
        uss = getUserStatsService();
        User u =us.getUser(handle);
        if( u == null)
            throw new Exception("User doesn't exist");
        VOUser vo = u.getVO();
        vo.setStats(uss.getStatsByUser(u).getVO());
        return vo;
    }


    public boolean login(String handle, String pass, boolean isAdmin) throws Exception
    {
        if(isAdmin)
        {
            as = getAdminService();
            Administrator a = as.getAdministrator(handle);
            if(a == null)
                throw new Exception("Admin does not exist");
            if(a.getPassword().compareTo(pass) != 0)
                throw new Exception("Invalid Password");
            return true;
        }
        else
        {
            us = getUserService();
            User u = us.getUser(handle);
            if(u == null)
                throw new Exception("User does not exist");
            if(u.getPassword().compareTo(pass) != 0)
                throw new Exception ("Invalid Password");
            return true;
        }

    }
    public VOProblem getProblemById(int id)throws Exception
    {
        ps = getProblemService();
        pss = getProblemStatsService();
        Problem p = ps.getProblem(id);
        if(p == null)
            throw new Exception("No problems found with id "+ id  );
        VOProblem vo = p.getVO();
        vo.setStats(pss.getStatsByProblem(p).getVO());
        return vo;
    }

    public VOProblem getProblemByName(String name)throws Exception
    {
        ps = getProblemService();
        pss = getProblemStatsService();
        Problem p = ps.getProblem(name);
        if(p == null)
            throw new Exception("No problems found with name "+name);
        VOProblem vo = p.getVO();
        vo.setStats(pss.getStatsByProblem(p).getVO());
        return vo;
    }


    public List<VOProblem> getProblemsByKeyword(String name)
    {
        ks = getKeyWordService();
        KeyWord k = ks.getKeyWord(name);
        List<VOProblem> l = new ArrayList<VOProblem>();
        for(Problem p : k.getProblemCollection())
            l.add(p.getVO());
        return l;
    }
    public List<VOProblem> listProblems()
    {
        ps = getProblemService();
        pss = getProblemStatsService();
        List<Problem> all = ps.getAllProblems();
        if(all == null)
            return null;
        List<VOProblem> l = new ArrayList<VOProblem>();
        List<ProblemStats> psl = pss.getAllStats();
        for(int i=0;i<all.size();i++)
        {
            VOProblem vop = all.get(i).getVO();
            vop.setStats(psl.get(i).getVO());
            l.add(vop);
        }
        return l;
    }

    public List<VOInstitution> listInstitutions()
    {
        is = getInstitutionService();
        List<Institution> all = is.getAllInstitutions();
        if(all == null)
            return null;
        List<VOInstitution> l = new ArrayList<VOInstitution>();
        for(Institution i : all)
            l.add(i.getVO());
        return l;
    }
    public VOUser getUserByHandle(String handle)throws Exception
    {
        us = getUserService();
        User u = us.getUser(handle);
        if(u == null)
            throw new Exception("User doesn't exist");
        return u.getVO();
    }


    public List<VOUser> getUsersByCountry(String name)
    {
        us = getUserService();
        List<VOUser> l = new ArrayList<VOUser>();
        List<User> lu = us.findUsersFromCountry(name);
        if(lu == null)
            return null;
        for(User u : lu)
            l.add(u.getVO());
        return l;
    }

    public List<VOUser> getUsersByInstitution(String name)
    {
        us = getUserService();
        List<VOUser> l = new ArrayList<VOUser>();
        List<User> lu = us.findUsersFromInstitution(name);
        if(lu == null)
            return null;
        for(User u : lu)
            l.add(u.getVO());
        return l;
    }
    public VOProblemStats getProblemStats(String pname)throws Exception
    {
        pss = getProblemStatsService();
        ps = getProblemService();
        Problem p = ps.getProblem(pname);
        if(p == null)
            throw new Exception("Problem doesn't exist");
        ProblemStats pst = pss.getStatsByProblem(p);
        if(p == null)throw new Exception("Stats don't exist");
        return pst.getVO();
    }
    public VOUserStats getUserStats(String handle)throws Exception
    {
        uss = getUserStatsService();
        us = getUserService();
        User u = us.getUser(handle);
        if(u == null)
            throw new Exception("User doesn't exist");
        UserStats ust = uss.getStatsByUser(u);
        if(u == null)
            throw new Exception("Stats don't exist");
        return ust.getVO();
    }
        public List<VOContest> listContests()
    {
        cts = getContestService();
        List<VOContest> l = new ArrayList<VOContest>();
        for(Contest c : cts.getAllContests())
            l.add(c.getVO());
        return l;
    }

    public boolean deleteContest(String contestID)throws Exception
    {
        cts = getContestService();
        Contest c = cts.getContest(contestID);
        if(c == null)
            throw new Exception("Contest doesn't exist");
        return cts.deleteContest(c);
    }

    public List<VOKeyWord> listKeyWords()
    {
        ks = getKeyWordService();
        List<VOKeyWord> l  = new ArrayList<VOKeyWord>();
        for(KeyWord k : ks.getAllKeyWords())
            l.add(k.getVO());
        return l;
    }


    public boolean registerUser(VOUser vo)throws Exception
    {
        us = getUserService();
        if(us.getUser(vo.getHandle())!=null)
            throw new Exception("Handle already in use ");
        return us.saveUser(vo);
    }

}